#include "Game.h"
#include "drawPrimitives.h"

//Objects *hokum = new Objects("Objects\\Hokum.obj");

/* this is called by std::sort to sort the list based on layerID 
 *  for drawing in the proper order 
 */
bool spriteSortingFunction(Sprite *s1, Sprite *s2)
{
	// return true if s1's layerID is less than s2's layerID
	return (s1->layerID < s2->layerID);
}

/* constructor */
Game::Game(void)
{
	/* background */
	stateInfo.bgClearColor.red = 0.5;
	stateInfo.bgClearColor.green = 0.5;
	stateInfo.bgClearColor.blue = 0.5;

	/* init state */
	stateInfo.gameState = STATE_GAMEPLAY;
	renderingTimer = new Timer("RENDER");
	updateTimer = new Timer("UPDATE");
}

/* destructor */
Game::~Game(void)
{
	/* deallocate memory and clean up here. if needed */
}

/* 
 * initializeGame()
 * - this function is called in the constructor to initialize everything related
 *   to the game, i..e loading sprites etc.
 * - MUST be called prior to any drawing/updating (you should add in checks to ensure this occurs in the right order)
 */
void Game::initializeGame()
{
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    float col[] = {1.0,1.0,1.0,1.0};
    glLightfv(GL_LIGHT0,GL_DIFFUSE,col);

    updateTimer = new Timer("Update");

    Level *level1 = new LevelOne();
    this->levelList.push_back(level1);

    currentLevel = levelList[0];
    currentLevel->init();

    moveBools.MOVE_FORWARD = moveBools.MOVE_BACKWARD = moveBools.MOVE_LEFT = moveBools.MOVE_RIGHT = false;
}

/* draw()
 * - this gets called automatically about 30 times per second
 * - this function just draws the sprites 
 */
void Game::draw()
{
	/* pre-draw - setup the rendering */
	PreDraw();

	/* draw - actually render to the screen */
	DrawGame();
	
	/* post-draw - after rendering, setup the next frame */
	PostDraw();
}

/*
 * Pre-Draw() is for setting up things that need to happen in order to draw
 *    the game, i.e. sorting, splitting things into appropriate lists etc.
 */
void Game::PreDraw()
{
	/* clear the screen */
	glViewport(0,0,stateInfo.windowWidth,stateInfo.windowHeight);
	glClearColor(stateInfo.bgClearColor.red, 
				 stateInfo.bgClearColor.green, 
				 stateInfo.bgClearColor.blue, 0);
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
	glLoadIdentity(); // clear out the transformation matrix

	// if we update our timer it will tell us the elapsed time since the previous 
	// frame that we rendered
	renderingTimer->tick();

	/* sort the sprites by layerID so we draw them in the right order */
	std::sort(spriteListToDraw.begin(), spriteListToDraw.end(), spriteSortingFunction);
}

/* 
 * DrawGame()
 *  - this is the actual drawing of the current frame of the game.
 */
void Game::DrawGame()
{
	/* here is where your drawing stuff goes */
    currentLevel->draw();

    drawTestPrimitives();
	drawSprites();

	glDisable(GL_TEXTURE_2D);
	
	/* this makes it actually show up on the screen */
	glutSwapBuffers();
}

/*
 * PostDraw()
 *  - in here you should clean up and set up things for the next frame
 *  - i.e. once I've used my assets, I can change them to set up things for
 *    the next frame, usually just memory management or setting up game state 
 *    boolean values etc.  
 */
void Game::PostDraw()
{
	// nothing here at the moment
}

/* drawSprites()
 * - this function is what actually draws the sprites
 *   onto the screen at their appropriate location
 * - it actually loops through a list of active sprites
 *   and then sorts them by their layerID and then draws them
 * - the sorting has to happen so that you draw from back to front
 *   just like a painter and a canvas.
 */
void Game::drawSprites()
{
	/* we could just do the following to draw the three sprites
	   but that would be silly since we have a list of sprites to draw
	   stored, so all we need to do is go through the list and draw eaach 
	   sprite in the list */
	/* // silly way 
	testSprite->draw();
	animatedSprite->draw();
	animatedSprite2->draw();
	*/
	
	/* better way */
	/* this is better because it doesn't matter how many sprites we have, they will always be drawn */
	std::vector<Sprite*>::iterator it; 
	for(it=spriteListToDraw.begin(); it != spriteListToDraw.end();it++)
	{
		Sprite *s = (*it);
		s->draw();
	}
}

/* for testing purposes to show you how to use
   the drawing primitives capabilities */
void Game::drawTestPrimitives()
{
    /*glLineWidth(5.0);
    glColor3f(1,0,0);
	glutWireCube(1.0);*/
}

/* update()
  - this function is essentially the game loop
    it gets called often and as such you
	don't actually need a "loop" to define the game
	it happens behind the scenes
  - think of this function as one iteration of the game loop
  - if you need to update physics calculations, sprite animation info,
    or sound etc, it goes in here
*/
void Game::update()
{
	// update our clock so we have the delta time since the last update
	updateTimer->tick();
    currentLevel->update();

    // handle mouse movements
    mouseMovementsToCamera();

    // Move Camera Position
    currentLevel->activeCamera->MoveCamera(0.1,moveBools,currentLevel->playerOne);

}

/* 
 * addSpriteToDrawList()
 * - this function simply pushes the sprite to the end of the list
 */
void Game::addSpriteToDrawList(Sprite *s)
{
	if(s)
	{
		/* push the sprite to the back of the list */
		this->spriteListToDraw.push_back(s);
	}
}


/*************************************************/
/* INPUT - keyboard/mouse functions below        */
/*************************************************/
/* keyboardDown()
   - this gets called when you press a key down
   - you are given the key that was pressed
     and where the (x,y) location of the mouse is when pressed
*/
void Game::keyboardDown(unsigned char key, int mouseX, int mouseY)
{
	switch(key)
	{
        // W
    case 119:
    case 87:
        moveBools.MOVE_FORWARD = true;
        //currentLevel->activeCamera->handleMouseMove(320,221,stateInfo);
        break;
        // S
    case 115:
    case 83:
        moveBools.MOVE_BACKWARD = true;
        //currentLevel->activeCamera->handleMouseMove(320,211,stateInfo);
        break;
        // A
    case 97:
    case 65:
        moveBools.MOVE_LEFT = true;
        break;
        // D
    case 100:
    case 68:
        moveBools.MOVE_RIGHT = true;
        //currentLevel->activeCamera->MoveCamera(0.1,key);
        break;
	case 32: // the space bar
        moveBools.MOVE_JUMP = true;
        break;
	case 27: // the escape key
	case 'q': // the 'q' key
		exit(1);
		break;
	}
}
/* keyboardUp()
   - this gets called when you lift a key up
   - you are given the key that was pressed
     and where the (x,y) location of the mouse is when pressed
*/
void Game::keyboardUp(unsigned char key, int mouseX, int mouseY)
{
    char temp;
	switch(key)
	{
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    case '0':
        temp = key;
        std::cout << "Camera Switch!\t Camera " << atoi(&temp) << std::endl;
        currentLevel->setActiveCamera(atoi(&temp));
        break;

    // W
    case 119:
    case 87:
        moveBools.MOVE_FORWARD = false;
        break;
    // S
    case 115:
    case 83:
        moveBools.MOVE_BACKWARD = false;
        break;
    // A
    case 97:
    case 65:
        moveBools.MOVE_LEFT = false;
        break;
    // D
    case 100:
    case 68:
        moveBools.MOVE_RIGHT = false;
        break;
    case 32: // the space bar
        moveBools.MOVE_JUMP = false;
        break;
	case 27: // the escape key
	case 'q': // the 'q' key
		exit(1);
		break;
	}
}

/*
 * mouseClicked
 * - this function is called when the mouse is clicked and it handles the 
 *   input state managment
 */
void Game::mouseClicked(int button, int state, int x, int y)
{
	if(state == GLUT_DOWN) 
	{
		input.mouseDown = true;
		input.clickX = x*stateInfo.ratioWidth;
		input.clickY = (stateInfo.windowHeight-y)*stateInfo.ratioHeight;
		input.button = button;

		switch(button)
		{
		case GLUT_LEFT_BUTTON:
			break;
		case GLUT_RIGHT_BUTTON:
            if(currentLevel->activeCamera->mode == CAM_FREE){
                glutSetCursor(GLUT_CURSOR_NONE);
                glutWarpPointer(stateInfo.windowWidth/2, stateInfo.windowHeight/2);
            }
            break;
		case GLUT_MIDDLE_BUTTON:
			break;
		}
	}
	else
	{
		input.mouseDown = false;
	}

}

/*
 * mouseMoved(x,y)
 * - this occurs only when the mouse is pressed down
 *   and the mouse has moved.  you are given the x,y locations
 *   in window coordinates (from the top left corner) and thus 
 *   must be converted to screen coordinates using the screen to window pixels ratio
 *   and the y must be flipped to make the bottom left corner the origin.
 */
void Game::mouseMoved(int x, int y)
{
	/* convert from window to screen pixel coordinates */
	input.currentX = x*stateInfo.ratioWidth;
	input.currentY = (stateInfo.windowHeight-y)*stateInfo.ratioHeight;
}

void Game::mouseMovementsToCamera()
{
    if(currentLevel->activeCamera->mode == CAM_FLW
        || currentLevel->activeCamera->mode == CAM_FPS)
    {
        glutSetCursor(GLUT_CURSOR_NONE);
        currentLevel->activeCamera->handleMouseMove(input.currentX,input.currentY,stateInfo, currentLevel->playerOne);
    }

    else if(!input.mouseDown){
        glutSetCursor(GLUT_CURSOR_LEFT_ARROW);
    }
	if(input.mouseDown)
	{
		if(input.button == GLUT_LEFT_BUTTON)
		{
            //Left click and move
		}
        if(input.button == GLUT_RIGHT_BUTTON && currentLevel->activeCamera->mode == CAM_FREE)
		{

            //Move Camera Focus
            currentLevel->activeCamera->handleMouseMove(input.currentX,input.currentY,stateInfo);
		}
	}
}